#include "CBL_IdMap.h"
#include "CBL_Math.h"
#include "CBL_Debug.h"
#include "CBL_PooledObject.h"

#include <algorithm>

CB_NAMESPACE_START

// ---------------------------------------------------------------------------------

class CIdItem : public Memory::CPooledObject
{
public:
	
	CIdItem(IdType _Id, const uint32 _Hash, const std::string& _rString)
	: m_Id(_Id)
	, m_Hash(_Hash)
	, m_Name(_rString)
	{
	}
	
public:
	
	IdType		m_Id;
	uint32		m_Hash;
	std::string m_Name;
};

// ---------------------------------------------------------------------------------

bool CIdMap::SSortIdOp::operator () (const CIdItem* _pLhs, const CIdItem* _pRhs)
{
	return _pLhs->m_Hash < _pRhs->m_Hash;
}

// ---------------------------------------------------------------------------------

CIdMap::CIdMap(uint32 _ContainerSize)
: m_Locked(false)
{
	m_ContainerSize = Math::NextPowerOf2(_ContainerSize);
	m_NameLookUp = new CIdItemSet[m_ContainerSize];
}

// ---------------------------------------------------------------------------------

CIdMap::~CIdMap()
{
	const CIdItemVector::const_iterator EndIter(m_IdLookUp.end());
	for (	CIdItemVector::const_iterator Iter(m_IdLookUp.begin());
			Iter != EndIter;
			++Iter)
	{
		delete(*Iter);
	}
	
	delete[] m_NameLookUp;
}

// ---------------------------------------------------------------------------------

void CIdMap::Lock()
{
	m_Locked = true;
}

// ---------------------------------------------------------------------------------

void CIdMap::Clear()
{
	const CIdItemVector::const_iterator EndIter(m_IdLookUp.end());
	for (	CIdItemVector::const_iterator Iter(m_IdLookUp.begin());
			Iter != EndIter;
			++Iter)
	{
		delete(*Iter);
	}
	
	m_IdLookUp.clear();
	
	for (uint32 i(0); i<m_ContainerSize; ++i)
	{
		m_NameLookUp[i].clear();
	}
	
	m_Locked = false;
}

// ---------------------------------------------------------------------------------

IdType CIdMap::RegisterId(const char* _pName)
{
	uint32 Crc = Math::CRC32(_pName);
	uint32 Slot = GetSlot(Crc);
	
	IdType ExistingId = GetId(_pName, Crc);
	
	if ((ExistingId == InvalidId) && !m_Locked)
	{
		ExistingId = m_IdLookUp.size();
		
		std::string Name(_pName);
		CIdItem* pNewItem = new CIdItem(ExistingId, Crc, Name);
		
		m_NameLookUp[Slot].insert(pNewItem);
		m_IdLookUp.push_back(pNewItem);
	}
	
	return ExistingId;
}

// ---------------------------------------------------------------------------------

void CIdMap::IIdOp::operator () (const CIdItem* _pItem)
{
	(*this)(_pItem->m_Id);
}

void CIdMap::ForEachId(CIdMap::IIdOp& _rOperator) const
{
	std::for_each<CIdItemVector::const_iterator, CIdMap::IIdOp&>(m_IdLookUp.begin(), m_IdLookUp.end(), _rOperator);
};

// ---------------------------------------------------------------------------------

const std::string& CIdMap::GetName(IdType _Id) const
{
	if ((_Id >= 0) && (_Id < (IdType)m_IdLookUp.size()))
		return m_IdLookUp[_Id]->m_Name;
	
	return InvalidIdString;
}

// ---------------------------------------------------------------------------------

struct SFindIdOp
{
	uint32		m_Hash;
	const char* m_pName;
	
	SFindIdOp(uint32 _Hash, const char* _pName)
	: m_Hash(_Hash)
	, m_pName(_pName)
	{}
	
	bool operator () (const CIdItem* _pItem)
	{
		return ((_pItem->m_Hash == m_Hash) && (_pItem->m_Name == m_pName));
	}
};

IdType CIdMap::GetId(const char* _pName, uint32 _Crc) const
{
	uint32 Slot = GetSlot(_Crc);
	const CIdItemSet& rSet = m_NameLookUp[Slot];
	
	CIdItemSet::const_iterator ItemIter = std::find_if(rSet.begin(), rSet.end(), SFindIdOp(_Crc, _pName));
	
	if (ItemIter != rSet.end())
		return (*ItemIter)->m_Id;
	
	return InvalidId;
}

CB_NAMESPACE_END